#include "Avalon/IOTests/SizeDeclarativeReaderTester.hpp"
#include "Avalon/IO/Buffer.hpp"
#include "Avalon/IO/BufferReader.hpp"
#include "Avalon/IO/PipedReader.hpp"
#include "Avalon/IO/PipedWriter.hpp"
#include "Avalon/IO/SizeDeclarativeReader.hpp"

using namespace Avalon;
using namespace Avalon::IO;
using namespace Avalon::IO::Tests;
using namespace Avalon::Threading;
using namespace boost;
using namespace boost::posix_time;
using namespace std;

void SizeDeclarativeReaderTester::TestEmptySource() {
  BufferReader bufferReader("");
  SizeDeclarativeReader reader(bufferReader);
  CPPUNIT_ASSERT(reader.IsAtEndOfDataSource());
}

void SizeDeclarativeReaderTester::TestSingleReadMessage() {
  PipedReader pipedReader;
  PipedWriter pipedWriter(&pipedReader);
  SizeDeclarativeReader reader(pipedReader);

  // Build the message.
  string message = "hello world";
  boost::uint32_t size = ToLittleEndian(
    static_cast<boost::uint32_t>(message.size()));
  pipedWriter.Write(&size, 4);
  pipedWriter.Write(message.c_str(), static_cast<int>(message.size()));
  CPPUNIT_ASSERT(!reader.IsAtEndOfDataSource());

  // Extract the message and test that it is equal to the original message.
  Buffer retrievedMessage;
  CPPUNIT_ASSERT(reader.Read(&retrievedMessage)->Get(seconds(1)) ==
    message.size());
  CPPUNIT_ASSERT(retrievedMessage.GetSize() == message.size());
  CPPUNIT_ASSERT(string(retrievedMessage.GetData(), message.size()) == message);
}

void SizeDeclarativeReaderTester::TestMultiReadMessage() {
  PipedReader pipedReader;
  PipedWriter pipedWriter(&pipedReader);
  SizeDeclarativeReader reader(pipedReader);

  // Build the message and write the first message fragment.
  string firstFragment = "hello";
  string secondFragment = " world";
  string message = firstFragment + secondFragment;
  boost::uint32_t size = ToLittleEndian(
    static_cast<boost::uint32_t>(message.size()));
  pipedWriter.Write(&size, 4);
  pipedWriter.Write(firstFragment.c_str(),
    static_cast<int>(firstFragment.size()));

  // Begin reading the message.
  Buffer retrievedMessage;
  Async<int>::Ptr readResult = reader.Read(&retrievedMessage);

  // Write the second message fragment.
  pipedWriter.Write(secondFragment.c_str(),
    static_cast<int>(secondFragment.size()));

  // Extract the message and test that it is equal to the original message.
  CPPUNIT_ASSERT(readResult->Get(seconds(1)) == message.size());
  CPPUNIT_ASSERT(retrievedMessage.GetSize() == message.size());
  CPPUNIT_ASSERT(string(retrievedMessage.GetData(), message.size()) == message);
}
